/*
 * Copyright (c) GoKeep by gokeep.org. 2022-2023. All rights reserved
 */

package org.gokeep.common.component;

import com.github.benmanes.caffeine.cache.Cache;
import jakarta.annotation.Resource;
import org.gokeep.common.constant.enums.ClientTypeEnum;
import org.gokeep.common.dto.UserAioSession;
import org.gokeep.common.dto.UserClientSessionAttr;
import org.gokeep.common.util.NetUtil;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.smartboot.socket.transport.AioSession;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

import static org.gokeep.common.constant.enums.CachePrefixEnum.CAFFEINE_SESSION_MAPPING;
import static org.gokeep.common.constant.enums.CachePrefixEnum.REDIS_USER_HOST_SESSION_ID;

@Component
public class SessionCacheComponent {

    @Resource
    protected Cache<String, UserAioSession> localSessionMappingCache;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 获取共享缓存的clientSession参数信息
     *
     * @return
     */
    public UserClientSessionAttr getShareWithClientSession(String userId, ClientTypeEnum clientType) {
        String redisKey = REDIS_USER_HOST_SESSION_ID.getCacheKey().formatted(userId, clientType);
        RBucket<UserClientSessionAttr> rBucket = redissonClient.getBucket(redisKey);
        return rBucket.get();
    }

    /**
     * 获取一个用户的所有客户端session描述信息
     *
     * @param userId
     * @return
     */
    public List<UserClientSessionAttr> getShareSessions(String userId) {
        return getShareSessions(Collections.singletonList(userId));
    }

    /**
     * 获取一个用户的所有客户端session描述信息
     *
     * @param userIds
     * @return
     */
    public List<UserClientSessionAttr> getShareSessions(List<String> userIds) {
        Set<String> cacheKeys = new HashSet<>();
        for (ClientTypeEnum clientType : ClientTypeEnum.values()) {
            for (String userId : userIds) {
                String cacheKey = REDIS_USER_HOST_SESSION_ID.getCacheKey().formatted(userId, clientType);
                cacheKeys.add(cacheKey);
            }
        }
        Map<String, UserClientSessionAttr> imap = redissonClient.getBuckets().get(cacheKeys.toArray(new String[0]));
        return new ArrayList<>(imap.values());
    }


    /**
     * 设置客户端共享缓存
     *
     * @param redisKey
     * @param value
     */
    public void putShareWithClientSession(String redisKey, UserClientSessionAttr value) {
        redissonClient.getBucket(redisKey).set(value);
    }


    /**
     * 设置客户端共享缓存
     *
     * @param clientType
     * @param userId
     * @param sessionId
     */
    public void putShareWithClientSession(ClientTypeEnum clientType, String userId, String sessionId) {
        UserClientSessionAttr value = new UserClientSessionAttr();
        value.setIp(NetUtil.getCurrentServerIp()).setSessionId(sessionId);
        String cacheKey = REDIS_USER_HOST_SESSION_ID.getCacheKey().formatted(userId, clientType);
        this.putShareWithClientSession(cacheKey, value);
    }

    /**
     * 设置本地的session关联信息
     *
     * @return
     */
    public void putLocalUserAioSession(String sessionId, AioSession session, String userId) {
        UserAioSession userAioSession = new UserAioSession().setUserId(userId).setSession(session);
        String caffeineKey = CAFFEINE_SESSION_MAPPING.getCacheKey().formatted(sessionId);
        localSessionMappingCache.put(caffeineKey, userAioSession);
    }


    /**
     * 客户端session映射关系
     *
     * @param userId
     * @param session
     */
    public void putLocalUserAioSession(AioSession session, String userId, ClientTypeEnum clientType) {
        UserAioSession userAioSession = new UserAioSession().setUserId(userId).setSession(session).setClientType(clientType);
        String cacheKey = CAFFEINE_SESSION_MAPPING.getCacheKey().formatted(session.getSessionID());
        localSessionMappingCache.put(cacheKey, userAioSession);
    }


    /**
     * 获取本地session映射的信息
     *
     * @param sessionId
     * @return
     */
    public UserAioSession getLocalUserAioSession(String sessionId) {
        String cacheKey = CAFFEINE_SESSION_MAPPING.getCacheKey().formatted(sessionId);
        return localSessionMappingCache.getIfPresent(cacheKey);
    }

    /**
     * 关闭本地session
     *
     * @param sessionId
     */
    public void closeAndRemoveLocalUserAioSession(String sessionId) {
        String cacheKey = CAFFEINE_SESSION_MAPPING.getCacheKey().formatted(sessionId);
        UserAioSession userAioSession = localSessionMappingCache.getIfPresent(cacheKey);
        String userId = userAioSession.getUserId();
        ClientTypeEnum clientType = userAioSession.getClientType();
        if (!userAioSession.getSession().isInvalid()) {
            userAioSession.getSession().close();
        }
        localSessionMappingCache.invalidate(cacheKey);
        String redissonKey = REDIS_USER_HOST_SESSION_ID.getCacheKey().formatted(userId, clientType);
        redissonClient.getBucket(redissonKey).delete();
    }


    /**
     * 获取当前用户的所有本地session
     *
     * @param userId
     * @return
     */
    public List<UserAioSession> getAllLocalSessionByUserId(String userId) {
        List<UserClientSessionAttr> clientSessionAttrs = getShareSessions(userId);
        return clientSessionAttrs.stream().map(localSessionAttr -> {
            String sessionId = localSessionAttr.getSessionId();
            return getLocalUserAioSession(sessionId);
        }).collect(Collectors.toList());
    }

    /**
     * 获取当前用户的所有本地session
     *
     * @param userIds
     * @return
     */
    public List<UserAioSession> getAllLocalSessionByUserId(List<String> userIds) {
        List<UserClientSessionAttr> clientSessionAttrs = getShareSessions(userIds);
        return clientSessionAttrs.stream().map(localSessionAttr -> {
            String sessionId = localSessionAttr.getSessionId();
            return getLocalUserAioSession(sessionId);
        }).collect(Collectors.toList());
    }


    /**
     * 对应sessionId是否在当前机器存在
     *
     * @param sessionId
     * @return
     */
    public boolean existLocalUserAioSession(String sessionId) {
        String cacheKey = CAFFEINE_SESSION_MAPPING.getCacheKey().formatted(sessionId);
        UserAioSession userAioSession = localSessionMappingCache.getIfPresent(cacheKey);
        if (userAioSession == null) {
            return false;
        }
        return true;
    }
}
